/*
 * eth_phy.h
 *
 *  Created on: Aug 1, 2013
 *      Author: Ken Arok
 *
 *
 * \brief Hardware Abstraction Layer of Ethernet PHY Management.
 *
 * Copyright (c) 2013 PT Hanindo Automation Solutions. All rights reserved.
 *
 */

#include "config_board.h"

#if BOARD_1_0_USED

#ifndef ETH_PHY_H_
#define ETH_PHY_H_

#include "board.h"
#include "gpio.h"
#include "macb.h"

#ifdef __cplusplus
extern "C" {
#endif


#if defined(ETHENET_PHY_DP83848)

#ifndef EXTPHY_PHY_ADDR
/*! Phy Address (set through strap options)
    This Define must be place in the board configuration
*/
#define EXTPHY_PHY_ADDR     0x01
#warning Set a default value : #define EXTPHY_PHY_ADDR 0x01
#endif

/*! Phy Identifier for the DP83848 */
#define EXTPHY_PHY_ID       0x20005C90


/*! \name Extended registers for DP83848
 */
//! @{
#define PHY_RBR             0x17    //!< RMII Bypass reg
#define PHY_MICR            0x11    //!< Interrupt Control reg
#define PHY_MISR            0x12    //!< Interrupt Status reg
#define PHY_PHYCR           0x19    //!< Phy CTRL reg
//! @}

/*! \name Generic MII registers.
 */
//! @{
#define PHY_BMCR            0x00        //!< Basic mode control register
#define PHY_BMSR            0x01        //!< Basic mode status register
#define PHY_PHYSID1         0x02        //!< PHYS ID 1
#define PHY_PHYSID2         0x03        //!< PHYS ID 2
#define PHY_ADVERTISE       0x04        //!< Advertisement control reg
#define PHY_LPA             0x05        //!< Link partner ability reg
//! @}

/*! \name Basic mode control register.
 */
//! @{
#define BMCR_RESV               0x007f  //!< Unused...
#define BMCR_CTST               0x0080  //!< Collision test
#define BMCR_FULLDPLX           0x0100  //!< Full duplex
#define BMCR_ANRESTART          0x0200  //!< Auto negotiation restart
#define BMCR_ISOLATE            0x0400  //!< Disconnect PHY from MII
#define BMCR_PDOWN              0x0800  //!< Powerdown the PHY
#define BMCR_ANENABLE           0x1000  //!< Enable auto negotiation
#define BMCR_SPEED100           0x2000  //!< Select 100Mbps
#define BMCR_LOOPBACK           0x4000  //!< TXD loopback bits
#define BMCR_RESET              0x8000  //!< Reset the PHY
//! @}

/*! RMII Bypass Register */
#define RBR_RMII            0x0020  //!< RMII Mode
/*! \name Interrupt Ctrl Register.
 */
//! @{
#define MICR_INTEN          0x0002  //!< Enable interrupts
#define MICR_INTOE          0x0001  //!< Enable INT output
//! @}

/*! \name Interrupt Status Register.
 */
//! @{
#define MISR_ED_INT_EN      0x0040  //!< Energy Detect enabled
#define MISR_LINK_INT_EN    0x0020  //!< Link status change enabled
#define MISR_SPD_INT_EN     0x0010  //!< Speed change enabled
#define MISR_DP_INT_EN      0x0008  //!< Duplex mode change enabled
#define MISR_ANC_INT_EN     0x0004  //!< Auto-Neg complete enabled
#define MISR_FHF_INT_EN     0x0002  //!< False Carrier enabled
#define MISR_RHF_INT_EN     0x0001  //!< Receive Error enabled
#define MISR_ED_INT         0x4000  //!< Energy Detect
#define MISR_LINK_INT       0x2000  //!< Link status change
#define MISR_SPD_INT        0x1000  //!< Speed change
#define MISR_DP_INT         0x0800  //!< Duplex mode change
#define MISR_ANC_INT        0x0400  //!< Auto-Neg complete
#define MISR_FHF_INT        0x0200  //!< False Carrier
#define MISR_RHF_INT        0x0100  //!< Receive Error
//! @}

/*! \name Phy Ctrl Register.
 */
//! @{
#define PHYCR_MDIX_EN       0x8000  //!< Enable Auto MDIX
#define PHYCR_MDIX_FORCE    0x4000  //!< Force MDIX crossed
//! @}

/*! \brief Generate an Hardware Reset on the Phy.
 *
 */
static inline void ethernet_phy_hw_reset(void)
{
	unsigned long _loop = 6600000;

	/* De-assert reset pin of PHY Low. */
	gpio_set_pin_low(EXTPHY_MACB_RESET_PIN);
	while(_loop) _loop--;
	/* Assert reset pin of PHY High. */
	gpio_set_pin_high(EXTPHY_MACB_RESET_PIN);
}

/*! \brief Generate a Software Reset on the Phy.
 *
 */
static inline void ethernet_phy_sw_reset(volatile avr32_macb_t *macb)
{
	volatile unsigned long config;

	// read Control register
	config = ulReadMDIO(macb, PHY_BMCR);
	config |= BMCR_RESET;
	// update ctrl register
	vWriteMDIO(macb, PHY_BMCR, config);

	// loop while link status isn't OK
	do {
		config = ulReadMDIO(macb, PHY_BMCR);
	} while (config & BMCR_RESET);
}

/*! \brief Setup the Phy in RMII mode.
 *
 */
static inline void ethernet_phy_setup_rmii(volatile avr32_macb_t *macb)
{
	volatile unsigned long mode;
	// read RBR
	mode = ulReadMDIO(macb, PHY_RBR);
	// set RMII mode if not done
	if ((mode & RBR_RMII) != RBR_RMII) {
		// force RMII flag if strap options are wrong
		mode |= RBR_RMII;
		vWriteMDIO(macb, PHY_RBR, mode);
	}
}

/*! \brief Setup auto-negotiation for the Phy.
 *
 */
static inline void ethernet_phy_setup_auto_negotiation(volatile avr32_macb_t *macb, volatile unsigned long *config)
{
	volatile unsigned long phy_ctrl;

	// read Phy Control register
	phy_ctrl = ulReadMDIO(macb, PHY_PHYCR);

	#if ETHERNET_CONF_AN_ENABLE
	#if ETHERNET_CONF_AUTO_CROSS_ENABLE
		// enable Auto MDIX
		phy_ctrl |= PHYCR_MDIX_EN;
	#else
		// disable Auto MDIX
		phy_ctrl &= ~PHYCR_MDIX_EN;
	#if ETHERNET_CONF_CROSSED_LINK
		// force direct link = Use crossed RJ45 cable
		phy_ctrl &= ~PHYCR_MDIX_FORCE;
	#else
		// force crossed link = Use direct RJ45 cable
		phy_ctrl |= PHYCR_MDIX_FORCE;
	#endif
	#endif

		// reset auto-negotiation capability
		*config |= (BMCR_ANRESTART | BMCR_ANENABLE);
	#else

		// disable Auto MDIX
		phy_ctrl &= ~PHYCR_MDIX_EN;
	#if ETHERNET_CONF_CROSSED_LINK
		// force direct link = Use crossed RJ45 cable
		phy_ctrl &= ~PHYCR_MDIX_FORCE;
	#else
		// force crossed link = Use direct RJ45 cable
		phy_ctrl |= PHYCR_MDIX_FORCE;
	#endif
		// clear AN bit
		*config &= ~BMCR_ANENABLE;

	#if ETHERNET_CONF_USE_100MB
		*config |= BMCR_SPEED100;
	#else
		*config &= ~BMCR_SPEED100;
	#endif
	#if ETHERNET_CONF_USE_FULL_DUPLEX
		*config |= BMCR_FULLDPLX;
	#else
	*config &= ~BMCR_FULLDPLX;
	#endif
	#endif

	// update Phy ctrl register
	vWriteMDIO(macb, PHY_PHYCR, phy_ctrl);
}

#else
#error No Ethernet PHYdefined!
#endif

#ifdef __cplusplus
}
#endif

#endif /* ETH_PHY_H_ */

#endif /* BOARD_1_0_USED */
